package com.example.jcextend.excel;

import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.support.ExcelTypeEnum;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.alibaba.excel.write.metadata.WriteTable;
import com.alibaba.excel.write.metadata.WriteWorkbook;
import com.example.jcextend.context.SpringContextUtils;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;

import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * 我们这个抽象类的作用,就是我们要使用模版的模式把EXCEL导出
 *  /把整体通用的逻辑抽出来亲
 */
@Slf4j
public abstract class BaseEasyExcelExport<T> {

    /**
     * 导出excel,支持大数据量的分批写入
     *  /传入的参数是文件名称和查询的条件,查询条件就是一定先去数据库查询的,在放到excel中的
     *  /我们通过这个map放条件
     */
    protected void exportExcel(String fileName, Map<String,Object> queryCondition){
        HttpServletResponse response = SpringContextUtils.getHttpServletResponse();

        //根据条件查询总记录数
        Long totalCount=dataTotalCount(queryCondition);

        //每一个Sheet页存放的记录数
        Long sheetDataRows=eachSheetTotalCount();

        //每次写入sheet的数据量
        Long writeDataRows=eachTimesWriteSheetTotalCount();

        //总数量小于sheet页的数量的话,sheet页的数据量就是我们总的数据量
        if (totalCount < sheetDataRows){
            sheetDataRows=totalCount;
        }

        //这里也是同理
        if (sheetDataRows < writeDataRows){
            writeDataRows = sheetDataRows;
        }

        doExport(response,fileName,queryCondition,totalCount,sheetDataRows,writeDataRows);
    }

    /**
     * 这三个方法我们要去调用者实现的
     */
    //每次写入sheet的数据量
    protected abstract Long eachTimesWriteSheetTotalCount();

    //        //每一个Sheet页存放的记录数
    protected abstract Long eachSheetTotalCount();

    //根据条件查询总记录数
    protected abstract Long dataTotalCount(Map<String, Object> queryCondition);

    /**
     * 导出excel
     */
    private void doExport(HttpServletResponse response, String fileName, Map<String, Object> queryCondition, Long totalCount, Long sheetDataRows, Long writeDataRows) {
        OutputStream outputStream = null;
        try {
            outputStream = response.getOutputStream();

            WriteWorkbook writeWorkbook = new WriteWorkbook();
            writeWorkbook.setOutputStream(outputStream);
            writeWorkbook.setExcelType(ExcelTypeEnum.XLSX);

            //专门用来写数据的
            ExcelWriter writer = new ExcelWriter(writeWorkbook);
            WriteTable table = new WriteTable();

            table.setHead(getExcelHead());

            //计算需要的Sheet的数量,以为大数据量我们一定是有很多的sheet的
            Long sheetNum=totalCount % sheetDataRows == 0 ? (totalCount/sheetDataRows) : (totalCount/sheetDataRows +1);

            //计算一般情况下每一个sheet需要写入的数量
            Long oneSheetWriterCount=totalCount > sheetDataRows ? sheetDataRows /writeDataRows :
                    totalCount % writeDataRows > 0 ? totalCount / writeDataRows + 1 : totalCount /writeDataRows;

            //计算最后一个sheet需要写入的次数
            Long lastSheetWriterCount=totalCount % sheetDataRows == 0 ? oneSheetWriterCount:
                    (totalCount  % sheetDataRows % writeDataRows == 0 ? (totalCount / sheetDataRows / writeDataRows):
                            (totalCount / sheetDataRows /writeDataRows +1));

            //分批查询分次写入
            ArrayList<Object> dataList = new ArrayList<>();
            for (int i=0; i<sheetNum; i++){
                WriteSheet sheet = new WriteSheet();

                sheet.setSheetNo(i);
                sheet.setSheetName(sheetNum == 1 ? fileName : fileName + i);

                for (int j=0; j < (i != sheetNum-1  || i==0 ? oneSheetWriterCount : lastSheetWriterCount); j++){
                    dataList.clear();
                    buildDataList(dataList,queryCondition,j + 1 + oneSheetWriterCount * i,writeDataRows);
                    writer.write(dataList,sheet,table);
                }

                response.setHeader("Content-Disposition","attachment;filename=" +
                        new String((fileName).getBytes("GB2312")
                        ,"ISO-8859-1")+".xlsx");

                response.setContentType("application/vnd.ms-excel");
                response.setCharacterEncoding("UTF-8");
                writer.finish();;

                outputStream.flush();
            }

        }catch (Exception e){
            log.error("错误");
        }finally {
            if (outputStream != null){
                try {
                    outputStream.close();
                }catch (Exception e){
                    log.error("错误");
                }
            }
        }
    }

    //这个表头数据我们也抽象出去,给用户去写
    protected abstract List<List<String>> getExcelHead();

    protected abstract void buildDataList(ArrayList<Object> dataList, Map<String, Object> queryCondition, long l, Long writeDataRows);
}
